# !/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2020/12/14 17:35
# @Author  : darknight
# @File    : main.py
# @Software: PyCharm
import inspect
import json
import os
import random
from binascii import hexlify
from concurrent.futures._base import as_completed
from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED
from Crypto.Cipher import AES
import base64

from core.spider import BaseSpider


class Spider(BaseSpider):
    NAME = 'netease'

    # 分类歌单的对应ID
    # 华语(1)、欧美(2)、韩语(3)、日语(4)、流行(5)、纯音乐(5)、摇滚(7)、电子(8)、民谣(9)
    # 分类: 原本分类
    SONGS_LIST_CATEGORY_ID = {
        1: "华语",
        2: "欧美",
        3: "韩语",
        4: "日语",
        5: "流行",
        6: "轻音乐",
        7: "摇滚",
        8: "电子",
        9: "民谣",
    }

    # 排行榜对应的ID
    # 飙升(1)、新歌(2)、热歌(3)、原创(4)、抖音(5)
    TOP_LIST_ID = {
        1: 19723756,
        2: 3779629,
        3: 3778678,
        4: 2884035,
        5: 2250011882,
    }

    def __init__(self):
        super().__init__()
        self.header_update({
            "Host": "music.163.com",
        })
        self.I = ''.join(random.sample('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 16))  # 16为随机数
        self.FIRST_KEY = '0CoJUm6Qyw8W8jud'
        self.ENC_KEY = '010001'
        self.MODULES = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'

    def sign(self, data):
        """
        获取加密的参数
        params是两次加密的
        :param song:
        :return:
        """
        res = self._get_params(data, self.FIRST_KEY)
        params = self._get_params(res, self.I)
        encSecKey = self._get_encSecKey()
        return {
            'params': params,
            'encSecKey': encSecKey
        }

    def _get_params(self, data, key):
        """
        获得params,加密字符长度要是16的倍数
        :param data:
        :param key:
        :return:
        """
        iv = '0102030405060708'
        num = 16 - len(data) % 16
        data = data + num * chr(num)  # 补足
        cipher = AES.new(key.encode(), AES.MODE_CBC, iv.encode())
        result = cipher.encrypt(data.encode())
        result_str = base64.b64encode(result).decode('utf-8')
        return result_str

    def _get_encSecKey(self):
        """
        获取encSecKey，256个字符串
        hexlify--->转换为btyes类型
        pow--->两个参数是幂,三个参数是先幂在取余
        format(rs, 'x').zfill(256)-->256位的16进制
        :return:
        """
        rs = pow(int(hexlify(self.I[::-1].encode('utf-8')), 16), int(self.ENC_KEY, 16), int(self.MODULES, 16))
        return format(rs, 'x').zfill(256)

    def get_recommend_songs_list(self):
        """
        推荐歌单
        :return:
        """
        data = self.sign(json.dumps({"limit": 10}))
        res = self.session.post('https://music.163.com/weapi/personalized/playlist', allow_redirects=False,
                                data=data).json()
        assert res['code'] == 200, f"推荐歌单获取失败。\n返回数据：{res}"
        return self.return_res(res)

    def get_recommend_songs(self):
        """
        推荐歌曲
        :return:
        """
        data = self.sign(json.dumps({"limit": 10}))
        res = self.session.post('https://music.163.com/weapi/personalized/newsong', allow_redirects=False,
                                data=data).json()
        assert res['code'] == 200, f"推荐歌曲获取失败。\n返回数据：{res}"
        return self.return_res(res)

    def get_songs_list_by_category(self, category_id):
        data = self.sign(json.dumps({"cat": f'{category_id}', "limit": 10}))
        res = self.session.post('https://music.163.com/weapi/playlist/list', allow_redirects=False, data=data).json()
        assert res['code'] == 200, f"分类歌单获取失败。\n返回数据：{res}"
        return self.return_res(res)

    def get_songs_list_detail(self, list_id):
        """
        歌单详情
        :param list_id:
        :return:
        """
        data = self.sign(json.dumps({"id": f'{list_id}'}))
        res = self.session.post('https://music.163.com/weapi/playlist/detail', allow_redirects=False, data=data).json()
        assert res['code'] == 200, f"歌单详情获取失败。\n返回数据：{res}"
        return self.return_res(res)

    def get_song_detail(self, song_mid):
        """
        歌曲详情
        :param song_mid:
        :return:
        """
        data = self.sign(
            json.dumps({"id": f'{song_mid}', "c": str([{'id': f'{song_mid}'}, {'id': f'{song_mid}'}])}))
        res = self.session.post('https://music.163.com/weapi/v3/song/detail', allow_redirects=False, data=data).json()
        assert res['code'] == 200, f"歌曲详情获取失败。\n返回数据：{res}"
        return self.return_res(res)

    def get_song_lyric(self, song_id):
        """
        歌曲详情
        :param song_id:
        :return:
        """
        data = self.sign(json.dumps({"id": f"{song_id}", "lv": -1, "tv": -1}))
        res = self.session.post('https://music.163.com/weapi/song/lyric', allow_redirects=False, data=data).json()
        assert res['code'] == 200, f"歌词获取失败。\n返回数据：{res}"
        return self.return_res(res)

    def get_song_play_url(self, song_mid):
        """
        歌曲播放地址
        :param song_mid:
        :return:
        """
        data = self.sign(
            json.dumps({"ids": f"[{song_mid}]", "level": "standard", "encodeType": "aac"}))
        res = self.session.post('https://music.163.com/weapi/song/enhance/player/url/v1', allow_redirects=False,
                                data=data).json()
        assert res['code'] == 200, f"歌曲播放地址获取失败。\n返回数据：{res}"
        return self.return_res(res)

    def get_mv_play_url(self, vid):
        """
        MV 播放地址
        m: 1080
            720
            360

        :param vid:
        :return:
        """
        _m = {
            40: 1080,
            30: 720,
            20: 480,
            10: 240,
        }

        def _get_url(k):
            data = self.sign(json.dumps({"id": f"{vid}", "r": f"{_m[k]}"}))
            res = self.session.post('https://music.163.com/weapi/song/enhance/play/mv/url', allow_redirects=False,
                                    data=data).json()
            if res["code"] == 200:
                return {k: res['data']['url']}
            else:
                return {k: ''}

        with ThreadPoolExecutor() as executor:
            data_list = []
            all_tasks = [executor.submit(_get_url, k) for k in _m]
            wait(all_tasks, return_when=ALL_COMPLETED)
            for future in as_completed(all_tasks):
                data_list.append(future.result())
            return self.return_res(data_list)

    def get_top_list(self, top_id):
        """
        歌曲排行榜
        :param top_id:
        :return:
        """
        data = self.sign(
            json.dumps({"id": f'{top_id}'}))
        res = self.session.post('https://music.163.com/weapi/playlist/detail', allow_redirects=False, data=data).json()
        assert res['code'] == 200, f"歌曲排行榜获取失败。\n返回数据：{res}"
        return self.return_res(res)

    def get_search_song(self, kw):
        """
        搜索歌曲
        :param kw:
        :param source:
        :return:
        """
        data = self.sign(
            json.dumps({"s": f'{kw}', "type": 1, 'limit': 10}))
        res = self.session.post('https://music.163.com/weapi/cloudsearch/pc', allow_redirects=False, data=data).json()
        # res = self.session.post('https://music.163.com/weapi/search/get', allow_redirects=False, data=data).json()
        assert res['code'] == 200, f"搜索歌曲获取失败。\n返回数据：{res}"
        return self.return_res(res)


if __name__ == '__main__':
    netease = Spider()
    _song_id = 1488737309
    _vid = 10730328
    print(netease.get_recommend_songs_list())
    # netease.get_recommend_songs()
    # netease.get_songs_list_by_category("华语")
    _list_id = 5204401228
    # netease.get_song_list_detail(_list_id)
    # netease.get_song_detail(_song_id)
    # netease.get_song_play_url(_song_id)
    # netease.get_song_lyric(_song_id)
    # netease.get_mv_play_url(_vid)
    _top_id = 19723756
    """
    飙升榜： 19723756
    新歌榜： 3779629
    热歌榜： 3778678
    原创榜： 2884035
    抖音榜： 2250011882
    """
    # netease.get_top_list(_top_id)
    _kw = '海阔天空'
    # netease.get_search_song(_kw)
